#include "image.h"

namespace SkyID
{

//- ImageJPEG -----------------------------------------------------------------

ImageJPEG::ImageJPEG()
{
	//
}

ImageJPEG::ImageJPEG(Reader *reader)
{
	setValid(open(reader));
}

bool ImageJPEG::open(Reader* reader)
{
	LOG_ASSERT(reader && reader->isValid());

	jpeg_decompress_struct cinfo;
	jpeg_error_mgr jerr;
	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_decompress(&cinfo);
	jpegSetCustomSource(&cinfo, reader);
	if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK)
	{
		LOG_MSG("ImageJPEG: wrong file signature");
		return false;
	}
	cinfo.out_color_space = JCS_RGB;

	// allocate memory for image data
	createImage(cinfo.image_width, cinfo.image_height, FORMAT_RGB);

	uint32 stride = mWidth * mFormat;
	JSAMPARRAY scnalines = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, stride, 1);
	jpeg_start_decompress(&cinfo);
	while (cinfo.output_scanline < cinfo.output_height)
	{
		jpeg_read_scanlines(&cinfo, scnalines, 1);
		memcpy(mData+(cinfo.output_scanline-1)*stride, scnalines[0], stride);
		// if error must (!) call jpeg_abort_decompress(&cinfo);
	}
	jpeg_finish_decompress(&cinfo);
	jpeg_destroy_decompress(&cinfo);
	return true;
}

void ImageJPEG::jpegInitSource(j_decompress_ptr cinfo)
{
	JpegCustomSource *src = reinterpret_cast<JpegCustomSource*>(cinfo->src);
	LOG_ASSERT(src);
	src->firstCall = true;
}

boolean ImageJPEG::jpegFillInputBuffer(j_decompress_ptr cinfo)
{
	JpegCustomSource *src = reinterpret_cast<JpegCustomSource*>(cinfo->src);
	LOG_ASSERT(src);
	size_t nbytes = src->reader->read(src->buffer, JPEG_BUFFER_SIZE);
	if (nbytes <= 0)
	{
		if (src->firstCall)
		{
			// fatal error
			LOG_ASSERT("ImageJPEG: error on read JPEG image");
		}
		LOG_MSG("ImageJPEG: end of file reached");
		src->buffer[0] = (JOCTET) 0xFF;
		src->buffer[1] = (JOCTET) JPEG_EOI;
		nbytes = 2;
	}
	src->pub.next_input_byte = src->buffer;
	src->pub.bytes_in_buffer = nbytes;
	src->firstCall = false;
	return TRUE;
}

void ImageJPEG::jpegSkipInputData(j_decompress_ptr cinfo, long num_bytes)
{
	JpegCustomSource *src = reinterpret_cast<JpegCustomSource*>(cinfo->src);
	LOG_ASSERT(src);
	if (num_bytes > 0)
	{
		while (num_bytes > (long)src->pub.bytes_in_buffer)
		{
			  num_bytes -= (long)src->pub.bytes_in_buffer;
			  jpegFillInputBuffer(cinfo);
		}
		src->pub.next_input_byte += (size_t)num_bytes;
		src->pub.bytes_in_buffer -= (size_t)num_bytes;
	}
}

void ImageJPEG::jpegTermSource(j_decompress_ptr cinfo)
{
	// in case we need terminate source, for instance, close socket
}

void ImageJPEG::jpegSetCustomSource(j_decompress_ptr cinfo, Reader *reader)
{
	JpegCustomSource *src = NULL;
	if (cinfo->src == NULL)
	{
		cinfo->src = (jpeg_source_mgr *)(*cinfo->mem->alloc_small)((j_common_ptr)cinfo,
			JPOOL_PERMANENT, sizeof(JpegCustomSource));
		src = reinterpret_cast<JpegCustomSource*>(cinfo->src);
		src->buffer = (JOCTET *)(*cinfo->mem->alloc_small)((j_common_ptr)cinfo,
			JPOOL_PERMANENT, JPEG_BUFFER_SIZE * sizeof(JOCTET));
	}
	src = reinterpret_cast<JpegCustomSource*>(cinfo->src);
	LOG_ASSERT(src);
	src->pub.init_source = jpegInitSource;
	src->pub.fill_input_buffer = jpegFillInputBuffer;
	src->pub.skip_input_data = jpegSkipInputData;
	src->pub.resync_to_restart = jpeg_resync_to_restart; // use default
	src->pub.term_source = jpegTermSource;
	src->pub.bytes_in_buffer = 0;
	src->pub.next_input_byte = NULL;
	src->reader = reader;
}

} // namespace SkyID
